home *** CD-ROM | disk | FTP | other *** search
/ Developer CD Series 1997 January: Mac OS SDK / Dev.CD Jan 97 SDK2.toast / Development Kits (Disc 2) / OpenDoc / OpenDoc Development / Debugging Support / OpenDoc™ Source Code / DocShell / ShellMem.cpp < prev    next >
Encoding:
C/C++ Source or Header  |  1996-08-28  |  10.5 KB  |  380 lines  |  [TEXT/MPS ]

  1. /*
  2.     File:        ShellMem.cpp
  3.  
  4.     Contains:    Memory management for the Shell
  5.  
  6.     Owned by:    Nick Pilch
  7.  
  8.     Copyright:    © 1995 - 1996 by Apple Computer, Inc., all rights reserved.
  9.  
  10.     Change History (most recent first):
  11.  
  12.          <8>     7/30/96    DH        Changed unknown macro/function BREAK to
  13.                                     WARN.
  14.          <7>     7/30/96    eeh        1372943: add ODShellCorruptDocGoodbye and
  15.                                     ODShellGenericGoodbye
  16.          <6>     7/28/96    DH        Bug#1372954: Changed LowMemoryAlert to call
  17.                                     ExitToShell instead of ODCloseDocument.
  18.                                     This was done because that utility routine
  19.                                     can crash under low memory situations.
  20.          <5>     6/21/96    jpa        T10002: Lowered min app heap free space to
  21.                                     32k, 6k contig.
  22.          <4>    .04.1996    NP        1339832: Send exit event before closing
  23.                                     documents/windows from LowMemoryAlert.
  24.          <3>      4/4/96    NP        1338241: Unstaticize function.
  25.          <2>     3/29/96    DM        1334273: show low mem quit alert and then
  26.                                     exit to shell when unable to show another
  27.                                     low mem alert due to out-of-mem err
  28.          <1>    10/24/95    jpa        first checked in
  29.     
  30.     In Progress:
  31.         
  32. */
  33.  
  34.  
  35. #ifndef _RLSHELL_
  36. #include "RlShell.h"
  37. #endif
  38.  
  39. #ifndef _SHELLDEF_
  40. #include "ShellDef.h"
  41. #endif
  42.  
  43. #ifndef _DISPTCH_
  44. #include <Disptch.xh>
  45. #endif
  46.  
  47. #ifndef _MEMMGR_
  48. #include <MemMgr.h>
  49. #endif
  50.  
  51. #ifndef _DLOGUTIL_
  52. #include <DlogUtil.h>
  53. #endif
  54.  
  55. #ifndef _DOCUTILS_
  56. #include <DocUtils.h>
  57. #endif
  58.  
  59. #ifndef _USERSRCM_
  60. #include <UseRsrcM.h>
  61. #endif
  62.  
  63. #ifndef _TEMPITER_
  64. #include <TempIter.h>
  65. #endif
  66.  
  67. #ifndef _SHELLMEM_
  68. #include <ShellMem.h>
  69. #endif
  70.  
  71. //==============================================================================
  72. // CONSTANTS
  73. //==============================================================================
  74.  
  75. const size_t kSlushFundSize            = 10 * 1024;    // Size of memory slush-fund
  76. const size_t kSlushFundAllocLimit    =  2 * 1024;    // Max request that will free slush fund
  77.  
  78. const ODSize kGoodAppFreeSpace =      32 * 1024;    // Sizes below which we notify the user
  79. const ODSize kGoodAppContigSpace =       6 * 1024;
  80. const ODSize kGoodTempFreeSpace =     100 * 1024;
  81. const ODSize kGoodTempContigSpace =   32 * 1024;
  82.  
  83. const ODSize kBailAppFreeSpace =       2 * 1024;    // App-heap mem requirement of low-mem alert
  84.  
  85.  
  86. //==============================================================================
  87. // METHODS
  88. //==============================================================================
  89.  
  90.  
  91. //-------------------------------------------------------------------------------------
  92. // InitMemory
  93. //-------------------------------------------------------------------------------------
  94.  
  95. void
  96. RealShell::InitMemory( )
  97. {
  98.     // Preload low-mem alerts so we'll be able to use them when space is low:
  99.     CUsingLibraryResources r;
  100.     ::Get1Resource('ALRT',kSHLphSpaceIsLow);
  101.     ::Get1Resource('DITL',kSHLphSpaceIsLow);
  102.     ::Get1Resource('ALRT',kSHLphAppSpaceIsLow);
  103.     ::Get1Resource('DITL',kSHLphAppSpaceIsLow);
  104.     
  105.     if( ! MMAllocateSlushFund(kDefaultHeapID, kSlushFundSize,kSlushFundAllocLimit) )
  106.         WARN("Could not allocate slush fund");
  107. }
  108.  
  109.  
  110. //-------------------------------------------------------------------------------------
  111. // IsFreeMemoryLow
  112. //-------------------------------------------------------------------------------------
  113.  
  114. inline ODSLong Max( ODSLong a, ODSLong b )
  115. {return a>b ?a :b;}
  116.     
  117. ODSize RealShell::IsFreeMemoryLow( ODBoolean &appIsLow, ODBoolean &tempIsLow )
  118. {
  119.     // First check/replenish the memory slush fund:
  120.     ODBoolean slushy;
  121.     if( MMSlushFundSize(kDefaultHeapID) > 0 )
  122.         slushy = kODTrue;
  123.     else
  124.         slushy = MMAllocateSlushFund(kDefaultHeapID, kSlushFundSize,kSlushFundAllocLimit);
  125.     
  126.     size_t free,contig;
  127.     ODSLong freeDelta, contigDelta;
  128.     ODSLong purgeApp = 0, purgeTemp = 0;
  129.     
  130.     MMSystemFreeSpace(kMMAppMemory, &free,&contig);
  131.     freeDelta = kGoodAppFreeSpace-free;
  132.     contigDelta = kGoodAppContigSpace-contig;
  133.     purgeApp = Max(freeDelta,contigDelta);
  134.     appIsLow = (purgeApp>0);
  135.     
  136.     MMSystemFreeSpace(kMMTempMemory, &free,&contig);
  137.     freeDelta = kGoodTempFreeSpace-free;
  138.     contigDelta = kGoodTempContigSpace-contig;
  139.     purgeTemp = Max(freeDelta,contigDelta);
  140.     tempIsLow = (purgeTemp>0);
  141.     
  142.     if( !slushy )
  143.         tempIsLow = kODTrue;
  144.     
  145.     purgeApp = Max(purgeApp,purgeTemp);
  146.     return (purgeApp>0 ?purgeApp :0);
  147. }
  148.  
  149.  
  150. //-------------------------------------------------------------------------------------
  151. // CheckFreeMemory
  152. //-------------------------------------------------------------------------------------
  153.  
  154. ODBoolean RealShell::CheckFreeMemory( )
  155. {
  156.     // If space is low, purge. If space is still low, alert the user, provided this
  157.     // process is active. After the user is alerted, set a flag so we don't put up
  158.     // an endless stream of alerts. Note that we don't use the normal dialog filter,
  159.     // since it calls the Dispatcher, which may cause trouble when space is low.
  160.     
  161.     ODBoolean appIsLow, tempIsLow;
  162.     ODSize purge = this->IsFreeMemoryLow(appIsLow,tempIsLow);
  163.     
  164.     if( purge ) {
  165.         // Low on free space, so purge:
  166.         this->Purge( 2*purge );
  167.         
  168.         this->IsFreeMemoryLow(appIsLow,tempIsLow);
  169.         
  170.         if( (appIsLow || tempIsLow) ) {
  171.             // Yow, still low on memory after purging.
  172.             size_t freeApp,contig;
  173.             MMSystemFreeSpace(kMMAppMemory, &freeApp,&contig);
  174.             if( !fLowMemNotified && fProcessIsActive && freeApp > kBailAppFreeSpace ) {
  175.                 this->LowMemoryAlert( tempIsLow );        // Notify user 1st time if we can
  176.                 fLowMemNotified = kODTrue;
  177.             }
  178.             return kODFalse;
  179.         }
  180.     }
  181.     fLowMemNotified = kODFalse;        // We're okay now, clear notification state
  182.     return kODTrue;
  183. }
  184.  
  185.  
  186. static ODBoolean
  187. DocNeedsSaving( Environment *ev, ODSession *session, ODDocument *document )
  188. {
  189.     // Throws no exceptions.
  190.     TRY{
  191.         return ODDocumentHasChanges(ev,session,document);
  192.     }CATCH_ALL{
  193.         WARN("ODDocumentHasChanges failed, err %d",ErrorCode());
  194.     }ENDTRY
  195.     return kODFalse;
  196. }
  197.  
  198. #if ODDebug
  199. void BREAK( const char[] );
  200. #endif
  201.  
  202. static void GenericAlert( short alertID )
  203. {
  204. #if ODDebug
  205.     WARN("about to show fatal low memory or related alert...");
  206. #endif
  207.     {
  208.         TRY{
  209.             CUsingLibraryResources r;
  210.             InitCursor();
  211.             ::Alert( alertID, kODNULL );
  212.         }CATCH_ALL{
  213.             WARN("cannot show fatal low memory alert - quitting...");
  214.         }ENDTRY
  215.     }
  216.     // make sure CUsingLibraryResources goes out of scope first...
  217.     ::ExitToShell(); // bye bye process
  218. }
  219.  
  220. void ODShellLowMemoryGoodbye()
  221. {
  222.     GenericAlert( kODAlertShellLowMemoryError );
  223. }
  224.  
  225. void ODShellGenericGoodbye( ODBoolean inform )
  226. {
  227.     if ( inform )
  228.         GenericAlert( kODAlertShellGenericError );
  229.     else
  230.         ::ExitToShell();
  231. }
  232.  
  233. void ODShellCorruptDocGoodbye()
  234. {
  235.     GenericAlert( kODAlertShellCorruptDocError );
  236. }
  237.  
  238. //------------------------------------------------------------------------------
  239. // DispatchExitEvent
  240. //
  241. //    Duplicated in ODSessnB.cpp
  242. //------------------------------------------------------------------------------
  243.  
  244. static void    DispatchExitEvent(Environment* ev, ODDispatcher* dispatcher)
  245. {
  246. // Copied from RealShell::FakePrintMenuEvent
  247.     ODEventData event;
  248.     event.message = 0L;
  249.     event.what = kODEvtExit;
  250.     // zero the rest of the fields
  251.     WASSERT( sizeof(Point) == sizeof(long) );
  252.     *(long*)&event.where = 0L;
  253.     event.when = 0L;
  254.     event.modifiers = 0;
  255.     dispatcher->Dispatch(ev, &event);
  256. }
  257.  
  258. //------------------------------------------------------------------------------
  259. // RealShell::LowMemoryAlert
  260. //------------------------------------------------------------------------------
  261.  
  262. void RealShell::LowMemoryAlert( ODBoolean tempMem )
  263. {
  264.     /*    Do not use the regular ODDialogFilter for this alert, as it calls back to the
  265.         Dispatcher, which calls other OpenDoc routines. This could be dangerous/fatal
  266.         in precisely this kind of low memory situation. */
  267.         
  268.     // First, do any open documents need to be saved?
  269.     ODBoolean needSave = kODFalse;
  270.     TRY{
  271.         for( TempODWindowIterator wi(fEV,fSession->GetWindowState(fEV)); wi; ++wi )
  272.             if( DocNeedsSaving(fEV,fSession, ODGetDraftOfWindow(fEV,wi)->GetDocument(fEV)) ) {
  273.                 needSave = kODTrue;
  274.                 break;
  275.             }
  276.     }CATCH_ALL{
  277.         if ( ErrorCode() == kODErrOutOfMemory )
  278.         {
  279.             ODShellLowMemoryGoodbye(); // goodbye cruel world
  280.         }
  281.         else
  282.         {
  283.             WARN("Error %d checking docs",ErrorCode());
  284.             // don't reraise
  285.         }
  286.     }ENDTRY
  287.  
  288.     ODSShort result; ODVolatile(result);
  289.     TRY{
  290.         // Shazam! Show the alert:
  291.         ODSShort id = needSave ?kSHLphSpaceIsLow :kSHLphSpaceIsLowNoSave;
  292.         if( tempMem ) id++;
  293.         {
  294.             CUsingLibraryResources r;
  295.             result = ShowAlert(fEV, id, kODNULL, fSession);
  296.         }
  297.     }CATCH_ALL{
  298.         if ( ErrorCode() == kODErrOutOfMemory )
  299.         {
  300.             ODShellLowMemoryGoodbye(); // goodbye cruel world
  301.         }
  302.         else
  303.         {
  304.             WARN("Error %d showing low mem alert",ErrorCode());
  305.             // don't reraise
  306.         }
  307.     }ENDTRY
  308.     
  309.     if( result == kStdCancelItemIndex )
  310.         return;
  311.     
  312.     needSave = needSave && (result==kStdOkItemIndex);    // Don't save if user said not to
  313.  
  314.     // CYBERDOG NEEDS THIS EXIT EVENT FIRST IF WE'RE GOING TO SHUT DOWN
  315.     //    EVERYTHING.
  316.     TRY
  317.         DispatchExitEvent(fEV, fSession->GetDispatcher(fEV));
  318.     CATCH_ALL
  319.         WARN("Caught error trying to dispatch exit event in "
  320.                 "RealShell::LowMemoryAlert");
  321.         if ( ErrorCode() == kODErrOutOfMemory )
  322.             ODShellLowMemoryGoodbye(); // quit immediately
  323.     ENDTRY
  324.     
  325.     // First close documents. (If we're told to save, close only the unmodified docs.)
  326.     // Note that this may free up memory before the save step below.
  327.     for( TempODWindowIterator wi(fEV,fSession->GetWindowState(fEV)); wi; ++wi ) {
  328.         TRY{
  329.             ODDocument *document = ODGetDraftOfWindow(fEV,wi)->GetDocument(fEV);
  330.             if( !needSave || !DocNeedsSaving(fEV,fSession, document) )
  331.                 // ODCloseDocument(fEV, fSession, document);
  332.                 // $$$$$ - dh
  333.                 /* This is a VERY TEMPORARY fix for the crashes on closing
  334.                    partially constructed documents. If you see this code,
  335.                    IMMEDIATELY file a bug and get this removed!!! 
  336.                 */
  337.                 ::ExitToShell();    // Will quit immediately.
  338.         }CATCH_ALL{
  339.             WARN("Error %d closing doc",ErrorCode());
  340.             // don't reraise
  341.         }ENDTRY
  342.     }
  343.     
  344.     if( needSave ) {
  345.         // Now save&close the rest of the documents.
  346.         for( TempODWindowIterator wi(fEV,fSession->GetWindowState(fEV)); wi; ++wi ) {
  347.             TRY{
  348.                 ODDocument *document = ODGetDraftOfWindow(fEV,wi)->GetDocument(fEV);
  349.                 if( DocNeedsSaving(fEV,fSession, document) ) {
  350.                     /*    Do not call RealShell::Save, as this will put up a standard-file dialog
  351.                         for an unsaved new document. Not the safest thing to do right now! */
  352.                     ODSaveDocument(fEV,fSession, document);
  353.                 }
  354.             }CATCH_ALL{
  355.                 WARN("Error %d saving/closing doc",ErrorCode());
  356.                 // don't reraise
  357.             }ENDTRY
  358.         }
  359.     }
  360.  
  361.     fSession->GetDispatcher(fEV)->Exit(fEV);        // Quit at next event loop
  362. }
  363.  
  364.  
  365. //-------------------------------------------------------------------------------------
  366. // Purge
  367. //-------------------------------------------------------------------------------------
  368.  
  369. ODSize    RealShell::Purge(ODSize size)
  370. {
  371.     ODSize result;
  372.     TRY{
  373.         result= fSession->Purge(fEV,size);
  374.     }CATCH_ALL{
  375.         result = 0;
  376.         // do not reraise, ignore exception
  377.     }ENDTRY
  378.     return result;
  379. }
  380.